#include "Managers/SkinsManager.h"
#include "Managers/GameManager.h"

#include <fstream>
#include <cstdio>

#include "Log.h"
#include "GameSingleton.h"

#include "Network/PackageManager.h"
#include "Network/Packages/ComponentClothingPackage.h"


#include "Utils/Utils.h"
#include "Utils/Hash.hpp"

#define DESTORY_FACTORY_TIME 100
#define REQUEST_TIME_OUT 30

namespace PocketSurvival
{
    static std::string skinDirPath;

    SkinsManager::SkinsManager() {}

    SkinsManager::~SkinsManager()
    {
        for (auto &pair : m_skinDataMap)
        {
            delete[] pair.second.skinBuff;
        }
    }

    void SkinsManager::loadGame()
    {
        skinDirPath = "./skins";
        if (std::filesystem::exists(skinDirPath) == false)
        {
            if (std::filesystem::create_directories(skinDirPath) == false)
            {
                throw std::runtime_error("检测不到玩家文件夹，并且无法创建新的玩家文件夹");
            }
        }
        else
        {
            // 遍历该文件夹下面的所有文件
            auto begin = std::filesystem::recursive_directory_iterator(skinDirPath); // 获取文件系统迭代器
            auto end = std::filesystem::recursive_directory_iterator();              // end迭代器

            // 这里只遍历出皮肤的名字
            for (auto it = begin; it != end; it++)
            {
                if (std::filesystem::is_regular_file(*it))
                {
                    std::string fileName = it->path().filename().string();

                    // Log::Info(fmt::format("发现本地的皮肤文件 {}", it->path().string()));
                    m_skinDirFileNames.insert(fileName);
                }
            }
        }

        // 这些是默认皮肤，不需要有真正的皮肤数据存在
        m_skinDirFileNames.insert(toHashSkinNameStr("$Male1"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Male2"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Male3"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Male4"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Female1"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Female2"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Female3"));
        m_skinDirFileNames.insert(toHashSkinNameStr("$Female4"));
    }

    /**
     * @brief 将皮肤名称字符串转成数字十六进制字符串，用于处理中文名称的问题
     * @param skinName 皮肤名称
     * @return 转换出来的字符串
    */
    std::string SkinsManager::toHashSkinNameStr(const std::string &skinName)
    {
        return fmt::format("{:X}", skinNameHash(skinName));
    }


    void SkinsManager::update(float dt)
    {
        m_cumulativeTime += dt;
        if (m_cumulativeTime >= 1.0f)
        {
            // 清理皮肤缓存
            bool needClear = false;
            m_cumulativeTime = 0;

            skimDataMapMutex.lock();
            // 计算销毁因子
            for (auto &pair : m_skinDataMap)
            {
                if (pair.second.destroyFactory > 0)
                {
                    pair.second.destroyFactory -= dt;
                }
                else
                {
                    delete[] pair.second.skinBuff;
                    pair.second.skinBuff = nullptr;
                    needClear = true;
                }
            }
            // 需要清理部分超时的皮肤数据
            if (needClear == true)
            {
                std::map<std::string, SkinData> skinMap;
                for (auto &pair : m_skinDataMap)
                {
                    if (pair.second.destroyFactory > 0)
                    {
                        skinMap[pair.first] = pair.second;
                    }
                }
                m_skinDataMap.swap(skinMap);
            }
            skimDataMapMutex.unlock();

            // 清理皮肤请求
            std::lock_guard<SpinLock> lk(requsetSkinMapLock);
            if (m_requestSkinMap.size() > 0)
            {
                bool needClearRequestMap = true;
                for (auto &pair : m_requestSkinMap)
                {
                    if (pair.second > 0)
                    {
                        pair.second -= dt;
                        needClearRequestMap = false;
                    }
                }
                if (true == needClearRequestMap)
                {
                    m_requestSkinMap.clear();
                }
            }
        }
    }

    /**
     * @brief 获取皮肤数据
     * @param skinName 皮肤名称
     * @return 皮肤数据，注： 若是 nullptr 则没有该皮肤数据
    */
    SkinData *SkinsManager::getSkinData(const std::string &skinName)
    {
        std::string hashSkinName = toHashSkinNameStr(skinName);

        // 先查找皮肤临时数据表中有没有该皮肤
        {
            skimDataMapMutex.lock();
            auto it = m_skinDataMap.find(hashSkinName);
            if (it != m_skinDataMap.end())
            {
                // 有的话，更新下销毁因子，让皮肤再保留一段时间
                it->second.destroyFactory = DESTORY_FACTORY_TIME;

                skimDataMapMutex.unlock();

                return &(it->second);
            }
            skimDataMapMutex.unlock();
        }

        SkinData *skinDataPtr = nullptr;

        // 临时缓存中没有的话，那就只能查找本地皮肤文件了
        std::string filePath = fmt::format("{}/{}", skinDirPath, hashSkinName);
        if (std::filesystem::exists(filePath) == false)
        {
            Log::Warn(fmt::format("皮肤文件 {} 不存在", filePath));
            return nullptr;
        }

        // 找到本地皮肤文件的话，需要先加载到临时数据表中，然后再发包
        std::ifstream inFile(filePath, std::ios::binary | std::ios::in);
        if (inFile.is_open())
        {
            std::filebuf *pbuf = inFile.rdbuf();

            SkinData skinData;
            skinData.skinSize = pbuf->pubseekoff(0, std::ios::end, std::ios::in);
            pbuf->pubseekpos(0, std::ios::in);
            skinData.skinBuff = new char[skinData.skinSize];
            pbuf->sgetn(skinData.skinBuff, skinData.skinSize);
            skinData.destroyFactory = DESTORY_FACTORY_TIME;

            pbuf->close();

            skimDataMapMutex.lock();
            m_skinDataMap[hashSkinName] = skinData;
            skinDataPtr = &(m_skinDataMap[hashSkinName]);
            skimDataMapMutex.unlock();

            Log::Info(fmt::format("加载皮肤 {} 成功, 对应文件: {}", skinName, filePath));
        }
        else
        {
            Log::Warn(fmt::format("加载皮肤 {} 失败, 对应文件: {}", skinName, filePath));
        }
        inFile.close();

        return skinDataPtr;
    }

    /**
     * @brief 添加皮肤数据，若是本地不存在的皮肤，还会进行保存，若是已经存在的皮肤则会报错
     * @param skinName 皮肤名称
     * @param skinData 皮肤数据
     * @note 传入的皮肤数据 (SkinData.skinBuff) 不能在外部释放内存，这里已经做了内存处理
    */
    void SkinsManager::addSkinData(const std::string &skinName, SkinData &skinData)
    {
        std::string hashSkinName = toHashSkinNameStr(skinName);

        m_skinDirFileNameMapLock.lock();
        // 判断下服务器是否存在该皮肤文件
        bool haveSkin = (m_skinDirFileNames.find(hashSkinName) != m_skinDirFileNames.end());
        m_skinDirFileNameMapLock.unlock();

        if (haveSkin == false)
        {
            // 添加进皮肤名称列表中
            m_skinDirFileNameMapLock.lock();
            m_skinDirFileNames.insert(hashSkinName);
            m_skinDirFileNameMapLock.unlock();

            // 添加销毁计数，100秒后没复用就销毁内存
            skinData.destroyFactory = DESTORY_FACTORY_TIME;

            // 然后再添加进皮肤数据临时缓存表中
            skimDataMapMutex.lock();
            m_skinDataMap[hashSkinName] = skinData;
            skimDataMapMutex.unlock();
            Log::Info(fmt::format("添加了新皮肤 {} => {}", skinName, hashSkinName));

            // 写入文件做长久保存
            std::ofstream outFile;
            outFile.open(fmt::format("{}/{}", skinDirPath, hashSkinName), std::ios::binary | std::ios::out);
            if (outFile.is_open())
            {
                outFile.write(skinData.skinBuff, skinData.skinSize);
            }
            else
            {
                Log::Warn(fmt::format("写入皮肤文件 {} => {} 失败", skinName, hashSkinName));
            }
            outFile.close();

            // 清理皮肤请求表
            requsetSkinMapLock.lock();
            auto it = m_requestSkinMap.find(hashSkinName);
            if (it != m_requestSkinMap.end())
            {
                m_requestSkinMap.erase(it);
            }
            requsetSkinMapLock.unlock();
        }
        else
        {
            // 存在皮肤文件，那么释放文件资源，然后再报错
            if (skinData.skinBuff != nullptr)
            {
                // 不释放的话会导致内存泄漏
                delete skinData.skinBuff;
            }
            throw std::logic_error(fmt::format("重复的皮肤文件 {} => {}", skinName, hashSkinName));
        }
    }


    /**
     * @brief 向特定的客户端请求皮肤文件
     * @param client 客户端对象
     * @param skinName 皮肤名称
    */
    void SkinsManager::requsetSkin(ClientPtr &client, const std::string &skinName)
    {
        std::string hashSkinName = toHashSkinNameStr(skinName);

        requsetSkinMapLock.lock();
        // 请求超时时间
        m_requestSkinMap[hashSkinName] = REQUEST_TIME_OUT;
        requsetSkinMapLock.unlock();

        std::shared_ptr<IPackage> package = std::make_shared<ComponentClothingPackage>(skinName, ClothingPackageType::WhoHas);
        package->client = client;
        package->sendType = PackageSendType::TARGET;
        GameSingleton::packageManager->addSendPackageToQueue(package);
    }


    /**
     * @brief 判断某个皮肤是否正在被请求上传中
     * @param skinName 皮肤名称
    */
    bool SkinsManager::haveRequsetSkin(const std::string &skinName)
    {
        std::string hashSkinName = toHashSkinNameStr(skinName);

        std::lock_guard<SpinLock> lk(requsetSkinMapLock);
        auto it = m_requestSkinMap.find(hashSkinName);
        if (it != m_requestSkinMap.end())
        {
            return (it->second > 0);
        }
        return false;
    }


    /**
     * @brief 判断某个皮肤是否存在
     * @param skinName 皮肤名称
    */
    bool SkinsManager::haveSkin(const std::string &skinName)
    {
        std::string hashSkinName = toHashSkinNameStr(skinName);

        std::lock_guard<SpinLock> lk(m_skinDirFileNameMapLock);
        return (m_skinDirFileNames.find(hashSkinName) != m_skinDirFileNames.end());
    }

} // namespace PocketSurvival
